// This file is part of OpenMeca, an easy software to do mechanical simulation.
//
// Author(s)    :  - Damien ANDRE  <openmeca@gmail.com>
//
// Copyright (C) 2012 Damien ANDRE
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.


#ifndef OpenMeca_Core_SetOfBase_hpp
#define OpenMeca_Core_SetOfBase_hpp



#include <cassert>
#include <list>
#include <iostream>

#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/list.hpp>


namespace OpenMeca
{
  namespace Core
  {

    template<typename type>
    class SetOfBase
    {
    public:
      static SetOfBase<type>& GetGlobalSet();
      typedef typename std::list<type *>::iterator it;
      typedef typename std::list<type *>::const_iterator const_it;

    public:

      SetOfBase();
      SetOfBase(const SetOfBase &);
      SetOfBase& operator=(const SetOfBase &);
      type& operator()(unsigned int);

      virtual ~SetOfBase();

      it Begin();
      it End();

      const_it Begin() const;
      const_it End() const;

      unsigned int GetTotItemNumber() const;

      void Clear();

      virtual void AddItem(type&);
      virtual void RemoveItem(type&);
      virtual void ClearAndDelete();
      //unsigned int GetRank (type *) const;
      unsigned int GetRank (const type *) const;
      bool Contain(type *) const;
      bool Contain(type &) const;
      
    private:
      friend class boost::serialization::access;
      template<class Archive>
      void serialize(Archive & ar, const unsigned int);
      
    protected:
      std::list<type *>  set_;
    };

    
    template<typename type> 
    template<class Archive>
    inline void
    SetOfBase<type>::serialize(Archive & ar, const unsigned int)
    {
      ar & BOOST_SERIALIZATION_NVP(set_);
    }

      
    template<typename type> 
    inline
    SetOfBase<type>::~SetOfBase()
    {
    }

    
    template<typename type> 
    inline
    SetOfBase<type>::SetOfBase(const SetOfBase &set)
      : set_(set.set_)
    {
    }
    
    template<typename type> 
    inline SetOfBase<type>& 
    SetOfBase<type>::operator=(const SetOfBase &set)
    {
      set_ = set.set_;
    }


    template<typename type> 
    inline type& 
    SetOfBase<type>::operator()(unsigned int i)
    {
      unsigned int index = 0;
      assert(i < GetTotItemNumber());
      typename std::list<type *>::iterator it;
      for (it=set_.begin() ; it != set_.end(); it++ )
	{
	  if (index == i)
	    return **it;
	  index++;
	} 
      assert(0);      
      return (**(Begin()));
    }


    template<typename type> 
    inline 
    SetOfBase<type>::SetOfBase()
      :set_()
					  
    {
    }
    
    template<typename type> 
    inline typename SetOfBase<type>::it 
    SetOfBase<type>::Begin()
    {
      return set_.begin();
    }

    template<typename type> 
    inline typename SetOfBase<type>::it 
    SetOfBase<type>::End()
    {
      return set_.end();
    }

    template<typename type> 
    inline typename SetOfBase<type>::const_it 
    SetOfBase<type>::Begin() const
    {
      return set_.begin();
    }

    template<typename type> 
    inline typename SetOfBase<type>::const_it 
    SetOfBase<type>::End() const
    {
      return set_.end();
    }

    
    template<typename type> 
    inline unsigned int 
    SetOfBase<type>::GetTotItemNumber() const
    {
      return set_.size();
    }
        
    template<typename type>
    inline void
    SetOfBase<type>::AddItem(type& item)
    {
      set_.push_back(&item);
    }
    

    template<typename type>
    inline void
    SetOfBase<type>::RemoveItem(type& item)
    {
      typename std::list<type *>::iterator it;
      for ( it=set_.begin() ; it != set_.end(); it++ )
	{
	  if (*it==&item)
	    {
	      set_.erase(it);
	      return;
	    }
	}
      assert(false);
    }

   
    template<typename type>
    inline void
    SetOfBase<type>::Clear()
    {
      set_.clear();
    }

    template<typename type>
    inline void
    SetOfBase<type>::ClearAndDelete()
    {
      typename std::list<type *>::const_iterator it;
      for ( it=set_.begin() ; it != set_.end(); it++ )
	{
	  delete (*it);
	}
      set_.clear();
    }

    template<typename type>
    inline unsigned int
    SetOfBase<type>::GetRank(const type * item) const
    {
      for (unsigned int i=0; i<GetTotItemNumber(); ++i)
	{
	  if (set_[i]==item)
	    {
	      return i;
	    }
	}
      assert(false); 
      return  0;
    }

    template<typename type>
    inline bool 
    SetOfBase<type>::Contain(type* item) const
    {
      typename std::list<type *>::const_iterator it;
      for ( it=set_.begin() ; it != set_.end(); it++ )
	{
	  if ((*it) == item)
	    return true;
	}
      return  false;
    }

     template<typename type>
    inline bool 
    SetOfBase<type>::Contain(type& item) const
    {
      return Contain(&item);
    }


  }
}

#endif
